﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using OrangeProject.ReactiveList.Core.Entities;

namespace OrangeProject.ReactiveList.Core.Client
{
    public class ReactiveList<T> : AbstractReactiveList, IList<T> where T : IEntity
    {
        public event Action<T[]> Updated;
        public event Action<T[]> Created;
        public event Action<T[]> Deleted;
        
        internal Action<T[]> _Updated;
        internal Action<T[]> _Created;
        internal Action<T[]> _Deleted;
        
        protected List<T> _internalCache;
        protected List<T> _insertQueue;
        protected List<T> _updateQueue;
        protected List<T> _deleteQueue;

        private Action<T, T> updateAction;
        
        private DataContext _context;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="_Name">ئىسمى</param>
        /// <param name="updateAction">(oldValue, newValue) => oldValue.prop = newValue.prop</param>
        public ReactiveList(DataContext context, Func<ReactiveList<T>, T> creator, String _Name, Action<T, T> updateAction)
        {
            _context = context;
            this.updateAction = updateAction;
            this.Name = _Name;
            Creator = () => creator(this);
            
            _internalCache = new List<T>();
            _insertQueue = new List<T>();
            _updateQueue = new List<T>();
            _deleteQueue = new List<T>();

            _Updated = obj => Updated?.Invoke(obj);
            _Created = obj => Created?.Invoke(obj);
            _Deleted = obj => Deleted?.Invoke(obj);
            
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _internalCache.ToList().GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _internalCache.ToArray().GetEnumerator();
        }

        public void Add(T item)
        {
            _insertQueue.Add(item);
        }

        public void Clear()
        {
            _deleteQueue.AddRange(_internalCache);
        }

        public bool Contains(T item)
        {
            return _internalCache.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            _internalCache.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            _deleteQueue.Add(item);
            return true;
        }

        public int Count => _internalCache.Count;

        public bool IsReadOnly { get; }

        public int IndexOf(T item)
        {
            return _internalCache.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            _insertQueue.Add(item);
        }

        public void RemoveAt(int index)
        {
            var value = _internalCache[index];
            var item = _insertQueue.SingleOrDefault(i => i.Id == value.Id);
            if (item != null) _insertQueue.Remove(value);
            item = _updateQueue.SingleOrDefault(i => i.Id == value.Id);
            if (item != null) _updateQueue.Remove(item);

            _deleteQueue.Add(value);
        }

        public T this[int index]
        {
            get => _internalCache[index];
            set { _updateQueue.Add(value); }
        }

        public void AttachUpdates(T value)
        {
            _updateQueue.Add(value);
        }
        
        IEntity[] ToArray(List<T> source) => source.Select(s => s as IEntity).ToArray();
        public override StateContainer<IEntity> LookupObjectStates()
        {
            
            var container = new StateContainer<IEntity>
            {
                Created =  ToArray(_insertQueue),
                Deleted = ToArray(_deleteQueue),
                Updated = ToArray(_updateQueue),
                Name = this.Name
            };
            return container;
        }
        
        public override void RestoreObjectAddDeleteStates(StateContainer<IEntity> container)
        {
            _deleteQueue.Clear();
            _insertQueue.Clear();
            
            
            _internalCache.AddRange(container.Created.Select(item => (T)item));
            
            foreach (var x1 in container.Deleted)
            {
                var item = _internalCache.Single(x => x.Id == x1.Id);
                _internalCache.Remove(item);
            }
        }

        public override void RestoreObjectUpdateState(StateContainer<IEntity> container)
        {
            _updateQueue.Clear();
            foreach (var x1 in container.Updated)
            {
                var item = _internalCache.Single(x => x.Id == x1.Id);
                updateAction(item, (T)x1);
            }
        }

        public override void DiscardChanges()
        {
            _deleteQueue.Clear();
            _insertQueue.Clear();
            _updateQueue.Clear();
        }
    }
}


